<?php
//$wxapi = new wechatCallbackAPI();
//$wxapi->valid(); //Application Message Interface
//$wxapi->responseMsg();

require_once "Component/wxBizMsgCrypt.php";
class wechatCallbackAPI{
	private $filepath; //凭证文件、log文件存放路径
	private $isEncrypt; //是否使用了加密
	public $WX_THIRD; //第三方开发平台对应WX_APPID的资料
	
	public function __construct() {
		$this->isEncrypt = false;
		$this->filepath = ''; //将缓存文件放在temp目录下
		if (defined('ROOT_PATH')) {
			$this->filepath = ROOT_PATH . "/temp/";
			//echo $this->filepath;
			if (!is_dir($this->filepath)) {
				$this->makeDir($this->filepath);
			}
		}
		$this->WX_THIRD = '';
		if (isset($GLOBALS['WX_THIRD'])) {
			$appid = $this->get_appid_from_url();
			if ($appid!='' && isset($GLOBALS['WX_THIRD'][$appid])) {
				$this->WX_THIRD = $GLOBALS['WX_THIRD'][$appid];
			}
		}
	}
	
	private function checkSignature(){
		$signature = isset($_GET['signature']) ? $_GET['signature'] : '';
		$timestamp = isset($_GET['timestamp']) ? $_GET['timestamp'] : '';
		$nonce = isset($_GET['nonce']) ? $_GET['nonce'] : '';
		$token = WX_TOKEN;
		$tmpArr = array($token, $timestamp, $nonce);
		sort($tmpArr, SORT_STRING);
		$tmpStr = implode( $tmpArr );
		$tmpStr = sha1( $tmpStr );
		if ($tmpStr == $signature) {
			return true;
		} else {
			return false;
		}
	}
	
	//生成随机串
	private function createNonceStr($length = 16) {
		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		$str = "";
		for ($i = 0; $i < $length; $i++) {
			$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
		}
		return $str;
	}
	
	//验证微信服务号
	public function valid(){
		$echoStr = isset($_GET['echostr']) ? $_GET['echostr'] : '';
		if ($this->checkSignature()) {
			echo $echoStr;
			exit;
		}
	}
	
	//通过网址获取AppID, 如 wxdbdf45b0e026c8ab
	public function get_appid_from_url(){
		$appid = '';
		$host = explode('.', $_SERVER['HTTP_HOST']);
		$uri = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
		if (substr($host[0], 0, 2)=='wx' && strlen($host[0])==18) {
			$appid = $host[0];
		} else if (substr($uri[0], 0, 2)=='wx' && strlen($uri[0])==18) {
			$appid = $uri[0];
		}
		return $appid;
	}
	
	//获取事件推送, callback为授权登录用
	public function responseMsg($callback = NULL){
		$this->check_auth($callback);
		$data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
		if (!empty($data)) {
			//判断POST信息是否加密信息
			$res = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
			if (isset($res->Encrypt)) {
				//$this->log('wxLog.txt', $data);
				$timeStamp = isset($_GET['timestamp']) ? $_GET['timestamp'] : '';
				$nonce = isset($_GET['nonce']) ? $_GET['nonce'] : '';
				$msg_signature = isset($_GET['msg_signature']) ? $_GET['msg_signature'] : '';
				if (!$timeStamp || !$nonce || !$msg_signature) die('MISSING PARAMETER');
				$this->isEncrypt = true;
				if (!is_array($this->WX_THIRD) && isset($res->AppId)) {
					$this->WX_THIRD = $this->get_third('appid', $res->AppId);
				}
				$decryptMsg = '';
				$token = $this->WX_THIRD ? $this->WX_THIRD['token'] : WX_TOKEN;
				$aeskey = $this->WX_THIRD ? $this->WX_THIRD['aeskey'] : WX_AESKEY;
				$appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : WX_APPID;
				$pc = new WXBizMsgCrypt($token, $aeskey, $appid);
				$errCode = $pc->decryptMsg($msg_signature, $timeStamp, $nonce, $data, $decryptMsg); //解密
				if ($errCode != 0) {
					$this->log('wxLog.txt', 'decryptMsg error: '.$errCode);
					echo $errCode;
					exit;
				}
				$data = $decryptMsg;
				$res = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
				if (isset($res->ComponentVerifyTicket)) {
					$this->component_verify_ticket($res->ComponentVerifyTicket);
				} else {
					$this->log('wxLog.txt', "decryptMsg:\n".$data);
				}
			}
			if (!$this->isEncrypt) $this->log('wxLog.txt', $data);
			//正常收取POST信息
			$toUserName = '';
			$fromUserName = '';
			$event = '';
			$content = '';
			$res = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
			if (isset($res->ToUserName)) $toUserName = $res->ToUserName;
			if (isset($res->FromUserName)) $fromUserName = $res->FromUserName;
			if (isset($res->Event)) $event = $res->Event;
			switch (strtolower($event)) {
				case 'click':
					if (isset($res->EventKey)) { //CUSTOM_VALUE
						if (function_exists('click')) $content = click($fromUserName, $res->EventKey);
					}
					break;
				case 'scan':
					if (isset($res->EventKey)) { //SCENE_ID
						if (function_exists('scan')) $content = scan($fromUserName, $res->EventKey);
					}
					break;
				case 'subscribe':
					if (function_exists('subscribe')) $content = subscribe($toUserName, $fromUserName);
					break;
				case 'unsubscribe':
					if (function_exists('unsubscribe')) $content = unsubscribe($toUserName, $fromUserName);
					break;
				default:
					$msgtype = '';
					if (isset($res->MsgType)) $msgtype = $res->MsgType;
					switch(strtolower($msgtype)){
						case 'text': //文本
							if (function_exists('msgText')) $content = msgText($toUserName, $fromUserName, trim($res->Content));
							//Content:文本消息内容
							break;
						case 'image': //图片
							if (function_exists('msgImage')) $content = msgImage($toUserName, $fromUserName, $res->PicUrl, $res->MediaId);
							//PicUrl:图片链接, MediaId:消息媒体id,可调用多媒体文件下载接口拉取数据
							//http://mp.weixin.qq.com/wiki/10/78b15308b053286e2a66b33f0f0f5fb6.html
							break;
						case 'voice': //语音
							if (function_exists('msgVoice')) $content = msgVoice($toUserName, $fromUserName, $res->Format, $res->MediaId);
							//Format:语音格式，如amr,speex
							break;
						case 'video': //视频
							if (function_exists('msgVideo')) $content = msgVideo($toUserName, $fromUserName, $res->ThumbMediaId, $res->MediaId);
							//ThumbMediaId:消息缩略图的媒体id,可调用多媒体文件下载接口拉取数据
							break;
						case 'location': //地理位置
							if (function_exists('msgLocation')) $content = msgLocation($toUserName, $fromUserName, $res->Location_Y, $res->Location_X, $res->Scale, $res->Label);
							//Location_Y:经度, Location_X:纬度, Scale:缩放大小, Label:地理位置信息
							break;
						case 'link': //链接
							if (function_exists('msgLink')) $content = msgLink($toUserName, $fromUserName, $res->Title, $res->Description, $res->Url);
							//Title:消息标题, Description:消息描述 ,Url:消息链接
							break;
						default:
							break;
					}
					if ($content=='' && function_exists('autoReply')) $content = autoReply($toUserName, $fromUserName);
					break;
			}
			if ($content!='') $this->sendTextMsg($fromUserName, $toUserName, $content);
		} else {
			exit('success');
		}
	}
	
	//事件推送
	public function sendTextMsg($toUserName, $fromUserName, $content){
		$tpl = "<xml>
		<ToUserName><![CDATA[%s]]></ToUserName>
		<FromUserName><![CDATA[%s]]></FromUserName>
		<CreateTime>%s</CreateTime>
		<MsgType><![CDATA[text]]></MsgType>
		<Content><![CDATA[%s]]></Content>
		</xml>";
		//<FuncFlag>1</FuncFlag>
		$str = sprintf($tpl, $toUserName, $fromUserName, time(), $content);
		//$this->log('wxLog.txt', $str);
		if ($this->isEncrypt) {
			$encryptMsg = '';
			$token = $this->WX_THIRD ? $this->WX_THIRD['token'] : WX_TOKEN;
			$aeskey = $this->WX_THIRD ? $this->WX_THIRD['aeskey'] : WX_AESKEY;
			$appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : WX_APPID;
			$nonce = $this->createNonceStr();
			$pc = new WXBizMsgCrypt($token, $aeskey, $appid);
			$errCode = $pc->encryptMsg($str, time(), $nonce, $encryptMsg);
			if ($errCode != 0) {
				$this->log('wxLog.txt', 'sendTextMsg error: '.$errCode);
				echo $errCode;
				exit;
			}
			$str = $encryptMsg;
		}
		echo $str;
		exit;
	}
	
	//获取access_token
	public function access_token($appid='', $secret='', $access_token_filename=''){
		if (!$access_token_filename) $access_token_filename = (($appid && $appid!=WX_APPID) ? $appid.'/' : '')."access_token.json";
		if ($appid=='') $appid = WX_APPID;
		if ($secret=='') $secret = WX_SECRET;
		$access_token = '';
		$expires_in = 0;
		$json = $this->getJson($access_token_filename, true);
		if (is_array($json)) {
			$access_token = $json['access_token'];
			$expires_in = intval($json['expires_in']);
		}
		if (time()>$expires_in || $access_token=='') {
			$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
			$html = file_get_contents($url);
			$json = json_decode($html, true);
			if (isset($json['errcode']))exit($json['errmsg']);
			$json['expires_in'] = intval($json['expires_in'])+time();
			$this->saveFile($access_token_filename, json_encode($json));
		}
		return $json;
	}
	
	//查询自定义菜单
	public function getMenu(){
		$arr = $this->access_token();
		$access_token = $arr['access_token'];
		$url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={$access_token}";
		$str = file_get_contents($url);
		$json = json_decode($str);
		if (is_null($json)) die('WEIXIN API ERROR');
		return $json;
	}
	
	//创建自定义菜单
	//https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
	public function setMenu($json){
		$arr = $this->access_token();
		$access_token = $arr['access_token'];
		$url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$access_token}";
		if (is_object($json)) $json = json_encode($json);
		$json = $this->postData($url, $json, true, true);
		if (is_null($json)) die('WEIXIN API ERROR');
		if ($json->errcode!=0) die($json->errmsg);
	}
	
	//删除自定义菜单
	public function deleteMenu(){
		$arr = $this->access_token();
		$access_token = $arr['access_token'];
		$url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={$access_token}";
		$str = file_get_contents($url);
		$json = json_decode($str);
		if (is_null($json)) die('WEIXIN API ERROR');
		if ($json->errcode!=0) die($json->errmsg);
	}
	
	//发送模板消息
	public function template_message($data){
		$dat = urldecode(json_encode($data));
		$arr = $this->access_token();
		$access_token = $arr['access_token'];
		$url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$access_token}";
		$res = $this->postData($url, $dat);
		return json_decode($res, true);
	}
	
	//检测是否有认证授权操作
	public function check_auth($callback = NULL){
		//普通微信号网页授权
		$weixin_auth = isset($_GET['weixin_auth']) ? $_GET['weixin_auth'] : '';
		if ($weixin_auth) {
			$json = $this->weixin_auth();
			if (function_exists($callback)) call_user_func($callback, $json);
			$html = $this->showSuccess('登录授权成功');
			exit($html);
			//exit('LOGIN SUCCESS');
		}
		//普通微信号网页授权(第三方开发平台引起)
		$mp_auth = isset($_GET['mp_auth']) ? $_GET['mp_auth'] : '';
		if ($mp_auth) {
			$json = $this->mp_auth();
			if (function_exists($callback)) call_user_func($callback, $json);
			exit('AUTH SUCCESS');
		}
		//电脑端打开进行公众号授权给第三方开发平台
		$component_auth = isset($_GET['component_auth']) ? $_GET['component_auth'] : '';
		if ($component_auth) {
			$authorizer_access_token = $this->component_auth();
			if (function_exists($callback)) call_user_func($callback, $json);
			$html = $this->showSuccess('登录授权成功');
			exit($html);
			//exit('AUTH SUCCESS');
		}
	}
	private function showSuccess($str){
		return '<meta charset="UTF-8">
				<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,initial-scale=1.0,user-scalable=no">
				<style>
				body{text-align:center;}
				div{background: url("https://res.wx.qq.com/open/zh_CN/htmledition/res/css/base/base_z2ef0cf.png") no-repeat 0 -1520px; width:64px; height:64px; margin:0 auto; margin-top:50px;}
				span{display:block; text-align:center; font-size:16px; line-height:50px;}
				a{display:block; margin:0 auto; margin-top:30px; width:100px; height:30px; line-height:30px; border-radius:4px; background:#3fb838; text-decoration:none; color:#fff;}
				</style>
				<div></div>
				<span>'.$str.'</span>
				<a href="javascript:WeixinJSBridge.call(\'closeWindow\')">返回</a>';
	}
	
	//POST数据
	public function postData($url, $data=NULL, $postJson=false, $returnJson=false, $assoc=false){
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
		if ($postJson) {
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
				'Content-Type: application/json; charset=utf-8',
				'Content-Length: ' . strlen($data))
			);
		}
		if (!empty($data)) {
			curl_setopt($ch, CURLOPT_POST, TRUE);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $data); //如果params为数组即使用multipart/form-data提交, 为字符串即使用application/x-www-form-urlencoded
		}
		curl_setopt($ch, CURLOPT_URL, $url);
		if (substr($url, 0, 8)=='https://') {
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); //对认证证书来源的检查
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //从证书中检查SSL加密算法是否存在
		}
		$output = curl_exec($ch);
		if ($output === false) {
			echo 'Curl error: ' . curl_error($ch);
			exit;
		}
		curl_close($ch);
		if ($returnJson) {
			$output = json_decode($output, $assoc);
			if (is_null($output)) $output = '';
		}
		return $output;
	}
	
	//创建文件夹, 支持创建多级目录
	public function makeDir($name, $mode=0777){
		if (is_dir($name)) return true;
		$name = str_replace('\\', '/', $name);
		$root_path = $_SERVER['DOCUMENT_ROOT'];
		if (defined('ROOT_PATH')) $root_path = ROOT_PATH;
		$root = str_replace('\\', '/', $root_path);
		$relative = str_replace($root, '', $name);
		$each = explode('/', $relative);
		$path = $root;
		foreach ($each as $p) {
			if ($p) {
				$path .= '/'.$p;
				if (!is_dir($path)) {
					if (mkdir($path, $mode)) {
						chmod($path, $mode);
						fclose(fopen($path . '/index.htm', 'w'));
					} else {
						return false;
					}
				} else {
					chmod($path, $mode);
				}
			}
		}
		return true;
	}
	
	//获取文件内容且转为json
	public function getJson($filename, $assoc=false){
		$str = $this->getFile($filename);
		$json = json_decode($str, $assoc);
		if (is_null($json)) return '';
		return $json;
	}
	
	//获取文件内容
	public function getFile($filename){
		$filename = $this->filepath.$filename;
		if (!file_exists($filename)) return '';
		return file_get_contents($filename);
	}
	
	//保存文件
	public function saveFile($filename, $content){
		$filename = $this->filepath.$filename;
		$path = $filename;
		$paths = explode('/', $path);
		$path = str_replace('/'.$paths[count($paths)-1], '', $path);
		$this->makeDir($path);
		$handle = fopen($filename, 'w') or die("UNABLE TO OPEN FILE");
		fwrite($handle, $content);
		fclose($handle);
		chmod($filename, 0777);
	}
	
	//删除文件,默认ACCESS_TOKEN缓存
	public function deleteFile($filename=''){
		if (!$filename) $filename = "access_token.json";
		$filename = $this->filepath.$filename;
		if (!file_exists($filename)) return false;
		return unlink($filename);
	}
	
	//下载媒体文件
	public function downloadFile($url){
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt($ch, CURLOPT_NOBODY, 0); //只取body头
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		$package = curl_exec($ch);
		$httpinfo = curl_getinfo($ch);
		curl_close($ch);
		return array('header'=>$httpinfo, 'body'=>$package);
	}
	
	//美化json字符串
	public function formatJSON($json, $indent=''){
		if (!isset($json)) return '';
		if ($indent=='') $indent = chr(9);
		if (!is_string($json)) $json = urldecode(json_encode($this->url_encode($json)));
		$str = str_replace('\\/', '/', $json);
		$result = '';
		$pos = 0;
		$line = "\n";
		$prevChar = '';
		$outOfQuotes = true;
		for ($i=0; $i<=strlen($json); $i++) {
			$char = substr($json, $i, 1);
			if ($char=='"' && $prevChar!='\\') {
				$outOfQuotes = !$outOfQuotes;
			} else if (($char=='}' || $char==']') && $outOfQuotes) {
				$result .= $line;
				$pos--;
				for ($j=0; $j<$pos; $j++) $result .= $indent;
			}
			$result .= $char;
			if (($char==',' || $char=='{' || $char=='[') && $outOfQuotes) {
				$result .= $line;
				if ($char=='{' || $char=='[') $pos++;
				for ($j=0; $j<$pos; $j++) $result .= $indent;
			}
			$prevChar = $char;
		}
		return $result;
	}
	
	//将对象/数组进行urlencode
	public function url_encode($obj){
		if (is_array($obj)) {
			foreach ($obj as $key=>$value) $obj[urlencode($key)] = $this->url_encode($value);
		} else if (is_object($obj)) {
			foreach($obj as $name=>$value) $obj->$name = $this->url_encode($obj->$name);
		} else if (is_string($obj)) {
			$obj = urlencode($obj);
		}
		return $obj;
	}
	
	//生成由数字,大写字母,小写字母组合的指定位数的随机数,str:指定字符,rndCode(8,"")
	public function rndCode($num, $str=''){
		if ($str=='') $str = '9zML5pGCkBAJQ2Zh4de1RlqNPno8m3FKijbrc6SDEas7O0TUXYtwxuVHWvIfgy'; //可使用中文字
		if (!is_numeric($num)) return '';
		$rndCode = '';
		$str = strval($str);
		$count = strlen($str);
		for ($o=1; $o<=$num; $o++) {
			$Rnd = mt_rand(0, $count-1);
			$rndCode .= substr($str, $Rnd, 1);
		}
		return $rndCode;
	}
	
	//记录LOG
	public function log($filename, $content){
		$filename = $this->filepath.$filename;
		file_put_contents($filename, date('Y-m-d H:i:s')."\n".$content."\n==============================\n\n", FILE_APPEND);
	}
	
	//第三方开发平台操作===============================================================================================
	//.htaccess 需要增加 RewriteRule ^wx\w{16}/(.+)$ /$1 [L]
	
	//保存微信服务器推送的component_verify_ticket
	private function component_verify_ticket($component_verify_ticket){
		$appid = $this->WX_THIRD ? $this->WX_THIRD['mp_appid'] : '';
		$component_verify_ticket_filename = $appid."/component_verify_ticket.txt";
		$this->saveFile($component_verify_ticket_filename, $component_verify_ticket);
		exit('success');
	}
	
	//获取component_access_token
	public function component_access_token(){
		$appid = $this->WX_THIRD ? $this->WX_THIRD['mp_appid'] : '';
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$secret = $this->WX_THIRD ? $this->WX_THIRD['secret'] : '';
		$component_verify_ticket_filename = $appid."/component_verify_ticket.txt";
		if (!$this->getFile($component_verify_ticket_filename)) die('NO COMPONENT_VERIFY_TICKET');
		$component_access_token_filename = $appid."/component_access_token.json";
		$access_token = '';
		$expires_in = 0;
		$json = $this->getJson($component_access_token_filename, true);
		if (is_array($json)) {
			$access_token = $json['component_access_token'];
			$expires_in = intval($json['expires_in']);
		}
		if (time()>$expires_in || $access_token==''){
			$url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
			$data = array();
			$data['component_appid'] = $component_appid;
			$data['component_appsecret'] = $secret;
			$data['component_verify_ticket'] = $this->getFile($component_verify_ticket_filename);
			$json = $this->postData($url, json_encode($data), true, true, true);
			$json['expires_in'] = intval($json['expires_in'])+time();
			$access_token = $json['component_access_token'];
			$this->saveFile($component_access_token_filename, json_encode($json));
		}
		return $access_token;
	}
	
	//获取预授权码pre_auth_code
	public function pre_auth_code(){
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$component_access_token = $this->component_access_token();
		if (!$component_access_token) die('NO COMPONENT_ACCESS_TOKEN');
		$url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token={$component_access_token}";
		$data = array();
		$data['component_appid'] = $component_appid;
		$json = $this->postData($url, json_encode($data), true, true, true);
		$pre_auth_code = $json['pre_auth_code'];
		return $pre_auth_code;
	}
	
	//使用授权码(component_auth回调得到的auth_code)换取公众号的接口调用凭据(authorizer_access_token)和授权信息
	public function authorizer_access_token($auth_code){
		$appid = $this->WX_THIRD ? $this->WX_THIRD['mp_appid'] : '';
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$authorizer_access_token_filename = $appid."/authorizer_access_token.json";
		$access_token = '';
		$expires_in = 0;
		$json = $this->getJson($authorizer_access_token_filename, true);
		if (is_array($json)) {
			$access_token = $json['authorizer_access_token'];
			$expires_in = intval($json['expires_in']);
		}
		if (time()>$expires_in || $access_token==''){
			$component_access_token = $this->component_access_token();
			$url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token={$component_access_token}";
			$data = array();
			$data['component_appid'] = $component_appid;
			$data['authorization_code'] = $auth_code;
			$json = $this->postData($url, json_encode($data), true, true, true);
			$json = $json['authorization_info'];
			$json['expires_in'] = intval($json['expires_in'])+time();
			$access_token = $json['authorizer_access_token'];
			$this->saveFile($authorizer_access_token_filename, json_encode($json));
		}
		return $access_token;
	}
	
	//获取网页授权登录用的access_token(普通)
	public function auth_access_token($appid='', $secret='', $code='', $access_token_filename=''){
		if (!$access_token_filename) $access_token_filename = (($appid && $appid!=WX_APPID) ? $appid.'/' : '')."auth_access_token.json";
		if ($appid=='') $appid = WX_APPID;
		if ($secret=='') $secret = WX_SECRET;
		if ($code!='') {
			$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$appid}&secret={$secret}&code={$code}&grant_type=authorization_code";
			$html = file_get_contents($url);
			$json = json_decode($html, true);
			if (isset($json['errcode']))exit($json['errmsg']);
			$json['expires_in'] = intval($json['expires_in'])+time();
			$this->saveFile($access_token_filename, json_encode($json));
			return $json;
		}
		$access_token = '';
		$expires_in = 0;
		$json = $this->getJson($access_token_filename, true);
		if (is_array($json)) {
			$access_token = $json['access_token'];
			$expires_in = intval($json['expires_in']);
		}
		if (time()>$expires_in || $access_token=='') {
			$url = "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
			$urls = explode('?', $url);
			$url = urlencode($urls[0]."?weixin_auth=weixin_auth");
			header("Location:{$url}");
		}
		return $json;
	}
	
	//获取公众号的access_token
	public function mp_auth_access_token($code=''){
		$appid = $this->WX_THIRD ? $this->WX_THIRD['mp_appid'] : '';
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$access_token_filename = $appid."/mp_auth_access_token.json";
		if ($code!='') {
			$component_access_token = $this->component_access_token();
			$url = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid={$appid}&code={$code}&grant_type=authorization_code&component_appid={$component_appid}&component_access_token={$component_access_token}";
			$html = file_get_contents($url);
			$json = json_decode($html, true);
			//$this->log(wxLog.txt, json_encode($json));
			if (isset($json['errcode']))exit($json['errmsg']);
			$json['expires_in'] = intval($json['expires_in'])+time();
			$this->saveFile($access_token_filename, json_encode($json));
			return $json;
		}
		$access_token = '';
		$expires_in = 0;
		$json = $this->getJson($access_token_filename, true);
		if (is_array($json)) {
			$access_token = $json['access_token'];
			$expires_in = intval($json['expires_in']);
		}
		if (time()>$expires_in || $access_token=='') {
			$url = "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
			$urls = explode('?', $url);
			$url = urlencode($urls[0]."?mp_auth=mp_auth");
			header("Location:{$url}");
			exit;
		}
		return $json;
	}
	
	//电脑端打开进行公众号授权给第三方开发平台
	public function component_auth(){
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$authorizer_access_token = '';
		$code = isset($_GET['auth_code']) ? $_GET['auth_code'] : '';
		//先获取auth_code
		if ($code=='') {
			$component_auth = isset($_GET['component_auth']) ? $_GET['component_auth'] : '';
			$url = "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
			$urls = explode('?', $url);
			$redirect_uri = urlencode($urls[0]."?component_auth={$component_auth}");
			$pre_auth_code = $this->pre_auth_code();
			$url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid={$component_appid}&pre_auth_code={$pre_auth_code}&redirect_uri={$redirect_uri}";
			header("Location:{$url}");
			exit;
		} else {
			return $this->authorizer_access_token($code);
		}
	}
	
	//普通微信号网页授权(第三方开发平台引起)
	public function mp_auth(){
		$appid = $this->WX_THIRD ? $this->WX_THIRD['mp_appid'] : '';
		$component_appid = $this->WX_THIRD ? $this->WX_THIRD['appid'] : '';
		$code = isset($_GET['code']) ? $_GET['code'] : '';
		//先获取code
		if ($code=='') {
			$mp_auth = isset($_GET['mp_auth']) ? $_GET['mp_auth'] : '';
			$url = "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
			$urls = explode('?', $url);
			$redirect_uri = urlencode($urls[0]."?mp_auth={$mp_auth}");
			$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$appid}&redirect_uri={$redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE&component_appid={$component_appid}#wechat_redirect";
			header("Location:{$url}");
			exit;
		} else {
			return $this->mp_auth_access_token($code);
		}
	}
	
	//普通微信号网页授权
	public function weixin_auth(){
		$appid = WX_APPID;
		$secret = WX_SECRET;
		$code = isset($_GET['code']) ? $_GET['code'] : '';
		//先获取code
		if ($code=='') {
			$weixin_auth = isset($_GET['weixin_auth']) ? $_GET['weixin_auth'] : '';
			$url = "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
			$urls = explode('?', $url);
			$redirect_uri = urlencode($urls[0]."?weixin_auth={$weixin_auth}");
			$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$appid}&redirect_uri={$redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
			header("Location:{$url}");
			exit;
		} else {
			$json = $this->auth_access_token($appid, $secret, $code);
			$access_token = $json['access_token'];
			$openid = $json['openid'];
			$json = '';
			//获取用户信息
			$url = "https://api.weixin.qq.com/sns/userinfo?access_token={$access_token}&openid={$openid}&lang=zh_CN";
			$html = file_get_contents($url);
			//$this->log('wxLog.txt', $html);
			$json = json_decode($html);
			if (isset($json['errcode']))exit($json['errmsg']);
			$json->nickname = preg_replace('/\xEE[\x80-\xBF][\x80-\xBF]|\xEF[\x81-\x83][\x80-\xBF]/', '', $json->nickname);
			$json->nickname = addslashes($json->nickname);
			$sex = 1;
			if (strpos($html, '"sex":')!==false) $sex = $json->sex;
			if (is_numeric($sex)) $sex = intval($sex)==1 ? '男' : '女';
			$json->sex = $sex;
			return $json;
		}
	}
	
	//获取用户基本信息(包括UnionID机制)
	public function get_userinfo($access_token, $openid){
		$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$openid}&lang=zh_CN";
		$html = file_get_contents($url);
		//$this->log('wxLog.txt', $html);
		$json = json_decode($html, true);
		if (isset($json['errcode'])) exit($json['errmsg']);
		//$json['nickname'] = $this->removeEmoji($json['nickname']);
		$json['nickname'] = addslashes($json['nickname']);
		$sex = 1;
		if (isset($json['sex'])) $sex = $json['sex'];
		if (is_numeric($sex)) $sex = intval($sex)==1 ? '男' : '女';
		$json['sex'] = $sex;
		return $json;
	}
	public function removeEmoji($text){
		// Match Emoticons
		$regexEmoticons = '/[\x{1F600}-\x{1F64F}]/u';
		$text = preg_replace($regexEmoticons, '', $text);
		// Match Miscellaneous Symbols and Pictographs
		$regexSymbols = '/[\x{1F300}-\x{1F5FF}]/u';
		$text = preg_replace($regexSymbols, '', $text);
		// Match Transport And Map Symbols
		$regexTransport = '/[\x{1F680}-\x{1F6FF}]/u';
		$text = preg_replace($regexTransport, '', $text);
		// Match Miscellaneous Symbols
		$regexMisc = '/[\x{2600}-\x{26FF}]/u';
		$text = preg_replace($regexMisc, '', $text);
		// Match Dingbats
		$regexDingbats = '/[\x{2700}-\x{27BF}]/u';
		$text = preg_replace($regexDingbats, '', $text);
		return $text;
	}
	
	//根据key对应的值与对比值作比较, 相等即返回对应相关资料
	public function get_third($third_key, $contrast_value, $isArray=true){
		$data = NULL;
		if (isset($GLOBALS['WX_THIRD'])) {
			foreach ($GLOBALS['WX_THIRD'] as $apps) {
				if (is_array($apps) && isset($apps[$third_key]) && $apps[$third_key]==$contrast_value) {
					$data = $apps;
					break;
				}
			}
			if ($data) $data = json_decode(json_encode($data), $isArray);
		}
		return $data;
	}
	
	//把微信个人资料(部分)进行md5
	public function md5_userinfo($json){
		$md5 = '';
		if (is_string($json)) $json = json_decode($json, true);
		if (!is_array($json)) $json = json_decode(json_encode($json), true);
		if (is_array($json) && isset($json['nickname']) && isset($json['sex']) && isset($json['country'])
			&& isset($json['city']) && isset($json['province']) && isset($json['language'])) {
			if (is_numeric($json['sex'])) $json['sex'] = intval($json['sex'])==1 ? '男' : '女';
			$arr = array($json['nickname'], $json['sex'], $json['country'], $json['province'], $json['city'], $json['language']);
			$md5 = implode($arr);
			//$this->log(wxLog.txt, $md5);
			$md5 = md5($md5);
		}
		return $md5;
	}
}
